/*
Copyright (C) Ariff Ehsan, Abdul Hadi Fikri

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*  Author : Ariff Ehsan
 *  Date   : June 27, 2009
 */


#include <system.h>
#include <button/button.h>

button_t* button_head;
button_t* button_tail;

button_t* button_curr;
BOOL button_busy;

timer_t button_timer;
task_t button_task;


void BUTTON_TIMER_run(void);
void BUTTON_TASK_run(void);

void BUTTON_defaultEvent(uint8_t);


void BUTTON_init(void) {
	
	button_head = button_tail = NULL;
	button_busy = FALSE;
	
	TIMER_reg(&button_timer, BUTTON_TIMER_run);
	TASK_reg(&button_task, BUTTON_TASK_run);
}


void BUTTON_reg(button_t* b, uint8_t* port, uint8_t pin,
	void (*event)(uint8_t state)) {
	
	/* set initial button state */
	b->state = BUTTON_RELEASED;
	
	/* set button pin */
	b->port = port;
	b->pin = pin;
	
	/* wire event function */
	if (event != NULL)
		b->event = event;
	else
		b->event = BUTTON_defaultEvent;
	
}


void BUTTON_start(button_t *b) {
	
	ENTER_CRITICAL();
	
	b->next = NULL;
	
	/* insert button into the list */
	if (button_head == NULL) {
		button_head = b;
		button_tail = b;
	}
	else {
		button_tail->next = b;
		button_tail = b;
	}
	
	EXIT_CRITICAL();
	
	if (!TIMER_isRunning(&button_timer))
		TIMER_startPeriodic(&button_timer, 20);
}


void BUTTON_TIMER_run(void) {
	
	if (button_busy)
		return;
	
	if (button_head == NULL)
		return;
	
	button_busy = TRUE;
	button_curr = button_head;
	
	TASK_post(&button_task);
	
}

void BUTTON_TASK_run(void) {
	
	uint8_t* port = button_curr->port;
	uint8_t pin = button_curr->pin;
	
	uint8_t t = (*port) & (1<<pin);
	
	switch (button_curr->state) {
	case BUTTON_RELEASED:
		if (t==0)
			button_curr->state = BUTTON_PUSHING;
		
		break;
		
	case BUTTON_PUSHING:
		button_curr->state = BUTTON_PUSHED;
		
		void (*event)(uint8_t s) = button_curr->event;
		(*event)(button_curr->state);
		
		break;
		
	case BUTTON_PUSHED:
		if (t>0)
			button_curr->state = BUTTON_RELEASING;
		
		break;
		
	case BUTTON_RELEASING:
		button_curr->state = BUTTON_RELEASED;
		
		void (*e)(uint8_t s) = button_curr->event;
		(*e)(button_curr->state);
		
		break;
		
	}
	
	button_curr = button_curr->next;
	
	if (button_curr != NULL)
		TASK_post(&button_task);
	else
		button_busy = FALSE;
	
}

void BUTTON_defaultEvent(uint8_t state) {
}
